Fork me on GitHub

Rhythm

package cas.cs4tb3.mellowd.primitives;

import cas.cs4tb3.mellowd.ArticulatedSound;
import cas.cs4tb3.mellowd.Beat;
import cas.cs4tb3.mellowd.PlayableSound;
import cas.cs4tb3.mellowd.TimingEnvironment;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

The Rhythm is responsible for the timing considerations in playable sounds. It specifies the duration in the form of a Beat. In a Mellow D source file a rhythm is a comma separated list of beats wrapped in < and >. Each beat is referred to by the first letter in it’s name. Additionally beats can be slurred together. This results in the durations overlapping and the notes connecting more smoothly.

public class Rhythm {

These two lists are maintained by the rhythm class making sure that both remain the same length. Each beat has a slurred value associated with it, true marking the beat as slurred and false being the default, un-slurred, state.

    private List<Beat> beats;
    private List<Boolean> slurred;

Creating a rhythm is done by specifying zero or more beats that make up the rhythm.

    public Rhythm(Beat... beats) {

A copy of the input data is stored in the beats list.

        this.beats = new ArrayList<>();
        Collections.addAll(this.beats, beats);

The slurred list is filled in with the default value keeping it the same size as the beats list.

        this.slurred = new ArrayList<>();
        for (int i = 0; i < this.beats.size(); i++) {
            slurred.add(false);
        }
    }

This utility method calculates the total duration of this rhythm if performed in the given timing environment. The returned duration is in ticks.

    public long getTotalTickDuration(TimingEnvironment environment) {

To calculate this duration simply sum up the result of calculating the ticks in each beat in this rhythm.

        long duration = 0;
        for (Beat beat : beats) {
            duration += environment.ticksInBeat(beat);
        }
        return duration;
    }

Rhythm also defines some methods for manipulating and retrieving the data stored in this class.

numBeats returns the number of beats in this Rhythm which is just the size of the beats list.

    public int numBeats() {
        return beats.size();
    }

getBeat returns the beat at a given position in the rhythm. This will throw an index out of bounds exception if the index is < 0 or ≥ numBeats().

    public Beat getBeat(int index){
        return beats.get(index);
    }

isSlurred checks if the beat at the given index is slurred. This returns true if the beat is slurred and false otherwise.

    public boolean isSlurred(int index) {
        return this.slurred.get(index);
    }

slur sets the slur flag to true for all beats in the index range [start, end). If end is larger than the number of beats in the rhythm this method will stop at the largest beat index available.

    public void slur(int start, int end) {
        for (int i = start; i < this.beats.size() && i < end; i++) {
            this.slurred.set(i, true);
        }
    }

append adds a beat to the rhythm. It is equivalent to calling append(beat, false) as the added beat will not be slurred.

    public void append(Beat beat) {
        this.beats.add(beat);
        this.slurred.add(false);
    }

appendSlurred adds a beat to the rhythm. It is equivalent to calling append(beat, true) as the added beat will not be slurred.

    public void appendSlurred(Beat beat) {
        this.beats.add(beat);
        this.slurred.add(true);
    }

append adds a beat to the rhythm setting the slurred flag to slur.

    public void append(Beat beat, boolean slur) {
        this.beats.add(beat);
        this.slurred.add(slur);
    }

append preforms a concatenation between this rhythm and the given rhythm. The order of the elements remain the same with each beat in the new rhythm being added to the end of this rhythm. All slurred beats will remain slurred.

    public void append(Rhythm rhythm) {
        this.beats.addAll(rhythm.beats);
        this.slurred.addAll(rhythm.slurred);
    }

append preforms a concatenation between this rhythm and the given rhythm. If the slurred flag is set to true then all appended beats will have their slurred flag set to true. If the flag is false then the beats in the given rhythm will retain their slurred state.

    public void append(Rhythm rhythm, boolean slurred) {
        this.beats.addAll(rhythm.beats);
        if (!slurred) {
            this.slurred.addAll(rhythm.slurred);
        } else {
            for (int i = 0; i < rhythm.slurred.size(); i++) {
                this.slurred.add(true);
            }
        }
    }

createPhrase is the method that preforms the * operation to turn a Melody and a rhythm into a Phrase.

    public Phrase createPhrase(Melody melody) {

First we need to take some space to save the created phrase. The length of the phrase is the length of the longer of the melody or rhythm.

        PlayableSound[] sounds = new PlayableSound[Math.max(this.beats.size(), melody.getNotes().size())];
        List<ArticulatedSound> notes = melody.getNotes();

Fill in the array by creating a new sound from the sound and beat. Iteration over the melody and rhythm are modulo so that the shorter collection wraps around to the front to make the sizes of the collections always match up.

        for (int i = 0; i < sounds.length; i++) {
            sounds[i] = new PlayableSound(notes.get(i % notes.size()),
                    this.beats.get(i % this.beats.size()),
                    this.slurred.get(i % this.slurred.size()));
        }

Create and return a phrase backed by the sounds just created.

        return new Phrase(sounds);
    }
}
h